package com.zhxy.controller.api.interceptor;

import java.io.IOException;
import java.util.Calendar;
import java.util.List;
import java.util.Random;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import com.zhxy.business.ApiLimitManager;
import com.zhxy.domain.ApiLimit;
import com.zhxy.domain.ApiLimitExample;
import com.zhxy.util.IPUtil;
import com.zhxy.util.RedisUtils;

/**
 * 流量监控拦截器
 * 使用redis作为存储，记录每个API的流量使用情况
 * 
 * @author xinyue.zheng
 */
public class FlowLimitInterceptor extends HandlerInterceptorAdapter {

    private static final Logger logger = LoggerFactory.getLogger(FlowLimitInterceptor.class);

    private List<String> specialUris;// 需要做特殊处理的地址
    @Autowired
    private ApiLimitManager apiLimitManger;

    @Override
    public boolean preHandle(HttpServletRequest req, HttpServletResponse res, Object handler) throws IOException {
        /*
         * 规则：
         * 获取流量：根据用户id,url,日期,ip为key，从redis中获取value1
         * 取限定量：根据url为key，从redis中获取value2
         * 
         * 假如value1为null => 需要从数据库里获取数据，并保存到redis中。
         * 为0 => 流量已经超。
         * 假如value2为null => 初始化为1。
         * 为非空 => 判断value1与value2的大小关系
         * value1 > value2+1 => 未超出流量
         * value1 = value2+1 => 未超出流量
         * value1 < value2+1 => 超出流量
         * 超出流量直接返回false，并且有输出错误信息
         * 未超出流量需要更新redis里的用量，并且返回true
         * 
         */
        // 获取限定量
        String api = getRequestAPI(req);
        Character lastChar = api.charAt(api.length() - 1);
        if (lastChar == '/' || lastChar == '\\') {
            api = api.substring(0, api.length() - 1);
        }
        String userid = (String) req.getSession().getAttribute("userid");
        userid = getRandomString(3);
        synchronized (this) {
            String limitStr = RedisUtils.get(api);
            Integer limit;
            Integer index;
            if (limitStr == null) {
                // 从mysql中获取api对应的限定量
                ApiLimitExample example = new ApiLimitExample();
                example.createCriteria().andApi_urlEqualTo(api);
                List<ApiLimit> list = apiLimitManger.selectByExample(example);
                if (list.isEmpty()) {
                    logger.info("数据库没有该api的限定量. 通过拦截.");
                    return true;
                } else {
                    limit = list.get(0).getLimit_count();
                    index = list.get(0).getId();
                }
                // 保存在redis中
                RedisUtils.save(api, limit.toString());
                RedisUtils.save(api + "_index", index.toString());
            } else if ("0".equals(limitStr)) {
                // 流量已超
                logger.error("流量已超,api的限定量是0,");
                return false;
            } else {
                limit = Integer.valueOf(limitStr);
            }
            Calendar now = Calendar.getInstance();
            Long ipLong = IPUtil.ipToLong(IPUtil.getRemoteHost(req));
            String indexStr = RedisUtils.get(api + "_index");
            String usedKey = now.get(Calendar.DAY_OF_MONTH) + "_" + userid + "_" + indexStr + "_" + ipLong;
            String usedFlowStr = RedisUtils.get(usedKey);
            if (usedFlowStr != null) {
                Integer usedFlow = Integer.valueOf(usedFlowStr) + 1;
                if (limit >= usedFlow) {
                    // 更新redis，返回true
                    RedisUtils.save(usedKey, usedFlow.toString());
                    return true;
                } else {
                    logger.error("api的调用已经超过限定,无法继续调用");
                    // 输出错误信息
                    // res.setStatus(200);
                    // res.setContentType("application/json;charset=UTF-8");
                    // res.getWriter().print("已经超过限定，无法继续调用");
                    res.sendError(HttpStatus.BAD_REQUEST.value(), "请求失败.");
                    return false;
                }
            } else {
                RedisUtils.save(usedKey, "1");
                return true;
            }
        }
    }

    private String getRequestAPI(HttpServletRequest req) {
        String requestURI = req.getRequestURI();
        int index = requestURI.indexOf('/', 1);
        return requestURI.substring(index);
    }

    public List<String> getSpecialUris() {
        return specialUris;
    }

    public void setSpecialUris(List<String> specialUris) {
        this.specialUris = specialUris;
    }

    public static String getRandomString(int length) { // length表示生成字符串的长度
        String base = "abcdefghijklmnopqrstuvwxyz0123456789";
        Random random = new Random();
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < length; i++) {
            int number = random.nextInt(base.length());
            sb.append(base.charAt(number));
        }
        return sb.toString();
    }
}
